package com.dl7.mvp.api;

import android.support.annotation.NonNull;
import android.widget.Toast;

import com.dl7.mvp.XApp;
import com.dl7.mvp.music.bean.MusicAlbumList;
import com.dl7.mvp.music.bean.MusicBean;
import com.dl7.mvp.music.bean.MusicCategoryList;
import com.dl7.mvp.music.bean.MusicInfo;
import com.dl7.mvp.video.bean.VideoCategoryList;
import com.dl7.mvp.video.bean.VideoAlbumList;
import com.dl7.mvp.video.bean.RcmdVideoAlbumList;
import com.dl7.mvp.video.bean.RcmdVideoList;
import com.dl7.mvp.video.bean.VideoInfo;
import com.dl7.mvp.utils.NetUtil;
import com.orhanobut.logger.Logger;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import okhttp3.Cache;
import okhttp3.CacheControl;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okio.Buffer;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;

/**
 * 整个网络通信服务的启动控制，必须先调用初始化函数才能正常使用网络通信接口
 */
public class RetrofitService {

    private static final String HEAD_LINE_NEWS = "T1348647909107";

    //设缓存有效期为1天
    static final long CACHE_STALE_SEC = 60 * 60 * 24 * 1;
    //查询缓存的Cache-Control设置，为if-only-cache时只查询缓存而不会请求服务器，max-stale可以配合设置缓存失效时间
    private static final String CACHE_CONTROL_CACHE = "only-if-cached, max-stale=" + CACHE_STALE_SEC;
    //查询网络的Cache-Control设置
    //(假如请求了服务器并在a时刻返回响应结果，则在max-age规定的秒数内，浏览器将不会发送对应的请求到服务器，数据由缓存直接返回)
    static final String CACHE_CONTROL_NETWORK = "Cache-Control: public, max-age=3600";
    // 避免出现 HTTP 403 Forbidden，参考：http://stackoverflow.com/questions/13670692/403-forbidden-with-java-but-not-web-browser
    static final String AVOID_HTTP403_FORBIDDEN = "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11";

    private static final String NEWS_HOST = "http://api.ergedd.com/"; //"http://c.3g.163.com/";

    private static INewsApi sNewsService;
    // 递增页码
    private static final int INCREASE_PAGE = 20;


    private RetrofitService() {
        throw new AssertionError();
    }

    /**
     * 初始化网络通信服务
     */
    public static void init() {
        // 指定缓存路径,缓存大小100Mb
        Cache cache = new Cache(new File(XApp.getContext().getCacheDir(), "HttpCache"),
                1024 * 1024 * 100);
        OkHttpClient okHttpClient = new OkHttpClient.Builder().cache(cache)
                .retryOnConnectionFailure(true)
                .addInterceptor(sLoggingInterceptor)
                .addInterceptor(sRewriteCacheControlInterceptor)
                .addNetworkInterceptor(sRewriteCacheControlInterceptor)
                .connectTimeout(10, TimeUnit.SECONDS)
                .build();

        Retrofit retrofit = new Retrofit.Builder()
                .client(okHttpClient)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .baseUrl(NEWS_HOST)
                .build();
        sNewsService = retrofit.create(INewsApi.class);
    }

    /**
     * 云端响应头拦截器，用来配置缓存策略
     * Dangerous interceptor that rewrites the server's cache-control header.
     */
    private static final Interceptor sRewriteCacheControlInterceptor = new Interceptor() {

//        @Override
//        public Response intercept(Chain chain) throws IOException {
//            Request request = chain.request();
//            if (!NetUtil.isNetworkAvailable(XApp.getContext())) {
//                request = request.newBuilder().cacheControl(CacheControl.FORCE_CACHE).build();
//                Logger.e("no network");
//            }
//            Response originalResponse = chain.proceed(request);
//
//            if (NetUtil.isNetworkAvailable(XApp.getContext())) {
//                //有网的时候读接口上的@Headers里的配置，你可以在这里进行统一的设置
//                String cacheControl = request.cacheControl().toString();
//                return originalResponse.newBuilder()
//                        .header("Cache-Control", cacheControl)
//                        .removeHeader("Pragma")
//                        .build();
//            } else {
//                return originalResponse.newBuilder()
//                        .header("Cache-Control", "public, " + CACHE_CONTROL_CACHE)
//                        .removeHeader("Pragma")
//                        .build();
//            }
//        }

        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();//获取请求
            //这里就是说判读我们的网络条件，要是有网络的话我么就直接获取网络上面的数据，要是没有网络的话我么就去缓存里面取数据
            if (!NetUtil.isNetworkAvailable(XApp.getContext())) {
                request = request.newBuilder()
                        //这个的话内容有点多啊，大家记住这么写就是只从缓存取，想要了解这个东西我等下在
                        // 给大家写连接吧。大家可以去看下，获取大家去找拦截器资料的时候就可以看到这个方面的东西反正也就是缓存策略。
                        .cacheControl(CacheControl.FORCE_CACHE)
                        .build();
                Logger.d("CacheInterceptor", "no network");
                Toast.makeText(XApp.getContext(), "没有网络", Toast.LENGTH_SHORT).show();
            }
            Response originalResponse = chain.proceed(request);
            if (NetUtil.isNetworkAvailable(XApp.getContext())) {
                //这里大家看点开源码看看.header .removeHeader做了什么操作很简答，就是的加字段和减字段的。
                String cacheControl = request.cacheControl().toString();
                return originalResponse.newBuilder()
                        //这里设置的为0就是说不进行缓存，我们也可以设置缓存时间
                        .header("Cache-Control", "public, max-age=" + 0)
                        .removeHeader("Pragma")
                        .build();
            } else {
                int maxTime = 4 * 24 * 60 * 60;
                return originalResponse.newBuilder()
                        //这里的设置的是我们的没有网络的缓存时间，想设置多少就是多少。
                        .header("Cache-Control", "public, only-if-cached, max-stale=" + maxTime)
                        .removeHeader("Pragma")
                        .build();
            }
        }
    };

    /**
     * 打印返回的json数据拦截器
     */
    private static final Interceptor sLoggingInterceptor = new Interceptor() {

        @Override
        public Response intercept(Chain chain) throws IOException {
            final Request request = chain.request();
            Buffer requestBuffer = new Buffer();
            if (request.body() != null) {
                request.body().writeTo(requestBuffer);
            } else {
                Logger.d("LogTAG", "request.body() == null");
            }
            //打印url信息
            Logger.w(request.url() + (request.body() != null ? "?" + _parseParams(request.body(), requestBuffer) : ""));
            final Response response = chain.proceed(request);

            return response;
        }
    };

    @NonNull
    private static String _parseParams(RequestBody body, Buffer requestBuffer) throws UnsupportedEncodingException {
        if (body.contentType() != null && !body.contentType().toString().contains("multipart")) {
            return URLDecoder.decode(requestBuffer.readUtf8(), "UTF-8");
        }
        return "null";
    }

    /************************************ API *******************************************/
    /**
     * 获取推荐视频列表
     *
     * @return
     */
    public static Observable<List<VideoInfo>> getRcmdVideoList(int offset, int limit) {
        return sNewsService.getRcmdVideoList(1, "new", offset, limit)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .flatMap(result -> {
                    List<VideoInfo> list = new ArrayList<>();
                    List<RcmdVideoList.DataBean> dataList = result.getData();
                    if (dataList != null && dataList.size() > 0) {
                        for (RcmdVideoList.DataBean data : dataList) {
                            list.add(new VideoInfo(data.getVideo()));
                        }
                    }
                    return Observable.just(list);
                });
    }

    // 获取推荐专辑列表
    public static Observable<List<VideoInfo>> getRcmdVideoAlbumList(int offset, int limit) {
        return sNewsService.getRcmdVideoAlbumListBanner("new", offset, limit)
                .subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .subscribeOn(AndroidSchedulers.mainThread())
                .observeOn(AndroidSchedulers.mainThread())
                .flatMap(result -> {
                    List<VideoInfo> list = new ArrayList<>();
                    List<RcmdVideoAlbumList.VideoBean> dataList = result.getData();
                    if (dataList != null && dataList.size() > 0) {
                        for (RcmdVideoAlbumList.VideoBean data : dataList) {
                            list.add(new VideoInfo(data));
                        }
                    }
                    return Observable.just(list);
                });
    }

    /**
     * 获取视频分类和具体的列表
     * http://api.ergedd.com//api/v1/album_categories?channel=new&offset=0&limit=100&addition_album_count=20
     */
    public static Observable<List<VideoCategoryList.CategoryBean>> getVideoCategoryList(int offset, int limit, int categoryCount) {
        return sNewsService.getVideoCategoryList("new", offset, limit, categoryCount)
                .subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .subscribeOn(AndroidSchedulers.mainThread())
                .observeOn(AndroidSchedulers.mainThread())
                .flatMap(result -> Observable.just(result.getData()));
    }

    // 获取专辑动画列表
    public static Observable<List<VideoInfo>> getAlbumDetailList(int id, int offset, int limit) {
        return sNewsService.getAlbumDetailList(id, "new", offset, limit)
                .subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .subscribeOn(AndroidSchedulers.mainThread())
                .observeOn(AndroidSchedulers.mainThread())
                .flatMap(albumsList -> {
                    List<VideoInfo> list = new ArrayList<>();
                    List<VideoAlbumList.AlbumBean> dataList = albumsList.getData();
                    if (dataList != null && dataList.size() > 0) {
                        for (VideoAlbumList.AlbumBean data : dataList) {
                            list.add(new VideoInfo(data));
                        }
                    }
                    return Observable.just(list);
                });
    }

    //=====================================

    /**
     * 获取儿歌专辑列表
     * http://api.ergedd.com//api/v1/album_categories?channel=new&offset=0&limit=100&addition_album_count=20
     */
    public static Observable<List<MusicAlbumList.DataBean>> getMusicAlbumList(int id, int offset, int limit) {
        return sNewsService.getMusicAlbumList(id, "new", offset, limit)
                .subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .subscribeOn(AndroidSchedulers.mainThread())
                .observeOn(AndroidSchedulers.mainThread())
                .flatMap(result -> Observable.just(result.getData()));
    }

    /**
     * 获取儿歌专辑推荐Banner
     * http://api.ergedd.com/api/v1/app_configs?types=promotion_audio_banner
     */
    public static Observable<List<MusicInfo>> getRcmdMusicAlbumListBanner() {
        return sNewsService.getRcmdMusicAlbumListBanner()
                .subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .subscribeOn(AndroidSchedulers.mainThread())
                .observeOn(AndroidSchedulers.mainThread())
                .flatMap(result -> {
                    List<MusicInfo> list = new ArrayList<>();
                    List<MusicAlbumList.DataBean> dataList = result.getData();
                    if (dataList != null && dataList.size() > 0) {
                        for (MusicAlbumList.DataBean data : dataList) {
                            list.add(new MusicInfo(data));
                        }
                    }
                    return Observable.just(list);
                });
    }

    /**
     * 儿歌精选专辑
     * http://api.ergedd.com/api/v1/audio_playlists/excellent?channel=original
     */
    public static Observable<List<MusicAlbumList.DataBean>> getRcmdMusicAlbumList() {
        return sNewsService.getRcmdMusicAlbumList()
                .subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .subscribeOn(AndroidSchedulers.mainThread())
                .observeOn(AndroidSchedulers.mainThread())
                .flatMap(result -> Observable.just(result.getData()));
    }

    /**
     * 儿歌分类列表(通过id可查询其他分类下的专辑列表)
     * http://api.ergedd.com/api/v1/audio_categories?channel=original
     */
    public static Observable<List<MusicCategoryList.DataBean>> getRcmdMusicCategoryList() {
        return sNewsService.getRcmdMusicCategoryList()
                .subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .subscribeOn(AndroidSchedulers.mainThread())
                .observeOn(AndroidSchedulers.mainThread())
                .flatMap(result -> Observable.just(result.getData()));
    }

    public static Observable<List<MusicInfo>> getMusicList(int id, int offset, int limit) {
        return sNewsService.getMusicList(id, offset, limit)
                .subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .subscribeOn(AndroidSchedulers.mainThread())
                .observeOn(AndroidSchedulers.mainThread())
                .flatMap(result -> {
                    List<MusicInfo> list = new ArrayList<>();
                    List<MusicBean.RecordBean.AudiosBean> dataList = result.getRecord().getAudios();
                    if (dataList != null && dataList.size() > 0) {
                        for (MusicBean.RecordBean.AudiosBean data : dataList) {
                            list.add(new MusicInfo(data));
                        }
                    }
                    return Observable.just(list);
                });
    }
}